package com.mango.iot.gateway.http.utils;

import lombok.extern.slf4j.Slf4j;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 验签工具类
 *
 * @author 53669
 * @since 2021/9/9 15:15
 */
@Slf4j
public final class SignatureUtil {

    /**
     * 定义加密方式，MAC算法可选以下算法：HmacMD5、HmacSHA1，需和signmethod一致。
     */
    private static final String HMAC_ALGORITHM_SHA = "hmacsha1";
    private static final String HMAC_ALGORITHM_MD5 = "hmacmd5";

    // 时间戳 15minute
    private final static long TIMESTAMP_DIFFERENT_RANGE = 900000;

    private static Set<String> SIGN_EXCLOUD_FIELD = new HashSet<String>();
    static {
        SIGN_EXCLOUD_FIELD.add("version");
        SIGN_EXCLOUD_FIELD.add("sign");
        SIGN_EXCLOUD_FIELD.add("signmethod");
    }

    public static boolean verifyTimestamp(long ts) {
        return Math.abs(System.currentTimeMillis() - ts) < TIMESTAMP_DIFFERENT_RANGE;
    }

    public static boolean verifySignature(Map<String, String> params, String secret, String signMethod,
            String signature) {
        return signature.equalsIgnoreCase(createSign(params, secret, signMethod));
    }

    public static String createSign(Map<String, String> params, String secret, String signMethod) {
        String content = createSignContent(params);
        log.info("sign content={}", content);

        String sign = encrypt(content, secret, signMethod);
        return sign;
    }

    /**
     * 将参数（除version、sign和signmethod外），按照英文字母升序，依次拼接排序（无拼接符号）
     *
     * @param params
     * @return
     */
    public static String createSignContent(Map<String, String> params) {
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);

        StringBuilder prestr = new StringBuilder();
        for (String key : keys) {
            if (!SIGN_EXCLOUD_FIELD.contains(key)) {
                String value = params.get(key);
                prestr.append(key).append(value);
            }
        }
        return prestr.toString();
    }

    /**
     * 使用HMAC_ALGORITHM加密。
     *
     * @param content，明文。
     * @param secret，密钥。
     * @return 密文。
     */
    private static String encrypt(String content, String secret, String signMethod) {
        String hmacSignMethod = getSignMethod(signMethod);
        try {
            byte[] text = content.getBytes(StandardCharsets.UTF_8);
            byte[] key = secret.getBytes(StandardCharsets.UTF_8);
            SecretKeySpec secretKey = new SecretKeySpec(key, hmacSignMethod);
            Mac mac = Mac.getInstance(secretKey.getAlgorithm());
            mac.init(secretKey);
            return byte2hex(mac.doFinal(text));
        } catch (Exception e) {
            log.error("[encrypt] hmacSignMethod={} error=", hmacSignMethod, e);
            return null;
        }
    }

    private static String getSignMethod(String signmethod) {
        if (HMAC_ALGORITHM_SHA.equalsIgnoreCase(signmethod)) {
            return HMAC_ALGORITHM_SHA;
        }
        return HMAC_ALGORITHM_MD5;
    }

    /**
     * 二进制转十六进制字符串。
     *
     * @param b，二进制数组。
     * @return 十六进制字符串。
     */
    private static String byte2hex(byte[] b) {
        StringBuffer sb = new StringBuffer();
        for (int n = 0; b != null && n < b.length; n++) {
            String stmp = Integer.toHexString(b[n] & 0XFF);
            if (stmp.length() == 1) {
                sb.append('0');
            }
            sb.append(stmp);
        }
        return sb.toString().toUpperCase();
    }

}
